﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ComLib.Logging;

namespace TwlNS {
	public partial class GUI : Form {
        

		private int NumOfSensorNode = 500;
		private int NumOfSinkNode = 1;
		private int FieldRadius = 500;
		private Environment Env=null;


		public GUI() {
			InitializeComponent();
			Properties.Settings.Default.PropertyChanged += Default_PropertyChanged;
			foreach (System.Configuration.SettingsProperty property in Properties.Settings.Default.Properties) {
				Default_PropertyChanged(Properties.Settings.Default, new PropertyChangedEventArgs(property.Name));
			}
			

			Logger.Default.Append(loggerControl.GetLogger());
		}

		void Default_PropertyChanged(object sender, PropertyChangedEventArgs e) {
			object value = Properties.Settings.Default[e.PropertyName];
			Properties.Settings.Default.Save();
			switch(e.PropertyName) {
				case "NumOfSensorNode":
					NumOfSensorNode = (int)value;
					break;
				case "NumOfSinkNode":
					NumOfSinkNode = (int) value;
					break;
				case "FieldRadius":
					FieldRadius = (int) value;
					break;
				case "RadioRadius":
					Environment.RadioRadius = (int) value;
					break;
				case "SensingRadius":
					Environment.SensingRadius = (int)value;
					break;
				case "HopCountThreshold":
					Node.HopCountThreshold = (int) value;
					break;
				case "ReverseHopCountThreshold":
					Node.ReverseHopCountThreshold = (bool)value;
					break;
				case "MaxChildren":
					Node.MaxChildren = (int) value;
					break;
				case "HopCountWeight":
					Environment.HopCountWeight = (int)value/100.0;
					Environment.EnergyWeight = 1 - Environment.HopCountWeight;
					NudEnergyWeight.Value = 100-(int)value;
					break;
				case "UseRandomVariableSeed":
					Environment.UseRandomVariableSeed = (bool) value;
					break;
				case "RandomVariableSeed":
					Environment.RandomVariableSeed = (int) value;
					break;
				case "DisplayAllLinks":
					GraphCanvas.DisplayAllLinks = (bool) value;
					graphCanvas.Refresh();
					break;
				case "DisplayPossibleParentLinks":
					GraphCanvas.DisplayPossibleParentLinks = (bool)value;
					graphCanvas.Refresh();
					break;
				case "DisplayNoneSinkedParentLinks":
					GraphCanvas.DisplayNoneSinkedParentLinks = (bool)value;
					graphCanvas.Refresh();
					break;
				case "DisplaySinkedParentLinks":
					GraphCanvas.DisplaySinkedParentLinks = (bool)value;
					graphCanvas.Refresh();
					break;
				case "DisplayActiveLinks":
					GraphCanvas.DisplayActiveLinks = (bool) value;
					graphCanvas.Refresh();
					break;
				case "DisplayAllNodes":
					GraphCanvas.DisplayAllNodes = (bool)value;
					graphCanvas.Refresh();
					break;
                case "InitialEnergy":
                    Node.InitialEnergy = (int)value;
                    break;
                case "SensingEnergyCost":
                    Node.SensingEnergyCost = (int)value;
                    break;
                case "ProcessingEnergyCost":
                    Node.ProcessingEnergyCost = (int)value;
                    break;
                case "SendingEnergyCost":
                    Node.SendingEnergyCost = (int)value;
                    break;
                case "ReceivingEnergyCost":
                    Node.ReceivingEnergyCost = (int)value;
                    break;
				case "MaxNumOfSets":
					Environment.MaxNumOfSets = (int) value;
					NudCurrentSet.Maximum = Environment.MaxNumOfSets-1;
					break;
				case "OriginalImplementation":
					Environment.OriginalImplementation = (bool) value;
					break;
				case "N2":
					Environment.N2 = (bool) value;
					break;
			}
		}

        Hashtable setSwitchTable;
		private void b_test_Click(object sender, EventArgs e){
			List<Node> nodes = new List<Node>();
			List<Node> sinkNodes = new List<Node>();
			for (int i = 0; i < NumOfSinkNode; i++){
				sinkNodes.Add(new SinkNode());
			}
			for (int i = 0; i < NumOfSensorNode; i++){
				nodes.Add(new SensorNode());

			}
			nodes.AddRange(sinkNodes);
			Env = new CircularField(FieldRadius);
			Env.AddNodesAndRandomDistribute(nodes);

			sinkNodes[0].Location = new Coord(Env.Width/2, Env.Height/2);
			if (sinkNodes.Count == 4){
				sinkNodes[0].Location = new Coord(Env.Width/4, Env.Height/4);
				sinkNodes[1].Location = new Coord(Env.Width*3/4, Env.Height/4);
				sinkNodes[2].Location = new Coord(Env.Width/4, Env.Height*3/4);
				sinkNodes[3].Location = new Coord(Env.Width*3/4, Env.Height*3/4);
			}

			Env.InitalizeNodes();

			DateTime t = DateTime.Now;
			Logger.Info("Graphing Result ...");
			Logger.Flush();
			graphCanvas.InitializeEnv(Env);
			NudCurrentSet_ValueChanged(null, null);
			graphCanvas.Refresh();

			//test();
			//return;
			//Canvas canvas = new Canvas(Env);
			//canvas.Show();

			ShowTimeConsumed(ref t);

			Logger.Info("Simulation ...");
			Logger.Flush();
			DateTime sim_start_time = DateTime.Now;
			
			int CheckStdevInterval = Properties.Settings.Default.CheckStdevInterval;
			DateTime checkStdevTime = sim_start_time.AddSeconds(CheckStdevInterval);
            int setSwitchingInterval = Properties.Settings.Default.SetSwitchingInterval;
            DateTime setSwitchingCheck = DateTime.Now;
            int setRecalcInterval = Properties.Settings.Default.SetRecalcInterval;
            DateTime setRecalcCheck = DateTime.Now;
            setSwitchTable = new Hashtable();
			while (true){
				bool br = false;
                if (setRecalcCheck.AddSeconds(setRecalcInterval) < DateTime.Now)
                {
                    Env.InitalizeNodes();
                    graphCanvas.Refresh();
                    foreach (Node n in nodes)
                    {
                        if (n is SensorNode && n.Sinked)
                        {
                            n.Initialization();
                        }
                    }
                    NudCurrentSet.Value = 0;
                    Logger.Info("Recalculating Sets ...");
                    setRecalcCheck = DateTime.Now;
                    setSwitchTable.Clear();
                }
				foreach (Node n in nodes){
					if(DateTime.Now>checkStdevTime){
						double t1 = ((DateTime.Now - sim_start_time).TotalMilliseconds/1000.0);
						Logger.Info("At time "+t1+" seconds");
						Env.CheckUsageVsHopCount(nodes);
						checkStdevTime=checkStdevTime.AddSeconds(CheckStdevInterval);
					}
					if (n is SensorNode && n.Sinked){
						if (n.Energy > 0){
							if (((SensorNode) n).CanRoute)
								n.Pulse();
                            if (setSwitchingCheck.AddSeconds(setSwitchingInterval) < DateTime.Now || !((SensorNode)n).CanRoute)
                            {
                                if (!((SensorNode)n).CanRoute)
                                {
                                    setSwitchTable.Add((int)NudCurrentSet.Value, "X");
                                    Logger.Info("Blacklisting set: " + NudCurrentSet.Value);
                                }
                                int next = CycleSets();
                                if (next == -1)
                                {
                                    br = true;
                                    break;
                                }
                                Logger.Info("Switching to Set: " + next);
                                Logger.Flush();
                                foreach (Node node in nodes)
                                {
                                    if (node is SensorNode && node.Sinked)
                                    {
                                        node.Initialization();
                                    }
                                }
                                setSwitchingCheck = DateTime.Now;
                            }
						}
					}
                    System.Threading.Thread.Sleep(1);
				}
				if (br)
					break;
			}

            Logger.Info(String.Format("Initial Packets: {0}, Success Packets: {1}, Dropped Packets: {2},", Environment.NumInitPackets, Environment.NumSuccessPackets, Environment.NumInitPackets - Environment.NumSuccessPackets));
			ShowTimeConsumed(ref sim_start_time);
			Env.CheckUsageVsHopCount(nodes);
			Console.WriteLine();
			Logger.Info("");
		}

        int CycleSets()
        {
            int next = NextGoodSetNum((int)NudCurrentSet.Value);
            if (next == -1)
                return -1;
            NudCurrentSet.Value = next;
            NudCurrentSet_ValueChanged(null, null);
            return next;
        }

        int NextGoodSetNum(int currentSetNum)
        {
            for (int i = 0; i < Environment.MaxNumOfSets ; i++)
            {
                if ((i > currentSetNum || (i == 0 && currentSetNum == NudCurrentSet.Maximum)) && !setSwitchTable.ContainsKey(i))
                {
                    return i;
                }
            }
            return -1;
        }

		void ShowTimeConsumed(ref DateTime startTime) {
			DateTime t2 = DateTime.Now;
			double totalms = (t2-startTime).TotalMilliseconds;
			Logger.Info("    Times Consumed: " + totalms/1000.0 + " seconds.");
			Logger.Flush();
			startTime = t2;
		}

		private void NudCurrentSet_ValueChanged(object sender, EventArgs e) {
			if(Env==null)
				return;
			Env.ActivateSet((int)NudCurrentSet.Value);
			graphCanvas.Refresh();
            
		}

        void test()
        {
            int[] sets = new int[] { 6, 8, 10, 12, 14 };
            foreach (int maxSets in sets)
            {
                Environment.MaxNumOfSets = maxSets;
                NudCurrentSet.Maximum = maxSets - 1;
                Env.DetermineSets();
                double sum = 0.0;
                for (int i = 0; i < maxSets; i++)
                {
                    NudCurrentSet.Value = i;
                    NudCurrentSet_ValueChanged(null, null);
                    sum += ((CircularField)Env).GetCoverage(2,i);
                }
                double avgCoverage = sum / maxSets;
                Logger.Info("" + avgCoverage + " " + Env.NumActiveNodes);
                Logger.Flush();
            }
			Logger.Info("Done...");
        }

        private void graphCanvas_Load(object sender, EventArgs e)
        {

        }

	}
}